﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Utils
{
    public class WheelTimer : IDisposable
    {
        private int _interval;
        private Thread _thread;
        private bool _threadRunning = false;
        private ConcurrentQueue<TimedAwaiter>[] _bucket;
        private ConcurrentQueue<TimedAwaiter> _queue;
        private int _tickCount = 20;
        private long _startTime = DateTime.Now.Ticks / 10000;
        private int _tick = 0;

        /// <summary>
        /// WheelTimer
        /// </summary>
        /// <param name="interval">时间间隔</param>
        public WheelTimer(int interval)
        {
            _interval = interval;
            _bucket = new ConcurrentQueue<TimedAwaiter>[_tickCount];
            _queue = new ConcurrentQueue<TimedAwaiter>();
            _threadRunning = true;

            for (int i = 0; i < _tickCount; i++)
            {
                _bucket[i] = new ConcurrentQueue<TimedAwaiter>();
            }

            _thread = new Thread(() =>
            {
                while (_threadRunning)
                {
                    long now = DateTime.Now.Ticks / 10000 - _startTime;
                    now = now % (_interval * _tickCount);
                    now = now % _interval;

                    Thread.Sleep((int)(_interval - now));

                    int index = _tick % _tickCount;
                    var queue = _bucket[index];
                    if (_tick < int.MaxValue - 1)
                    {
                        _tick++;
                    }
                    else
                    {
                        _tick = index + 1;
                    }

                    for (int i = 0; i < 100000; i++)
                    {
                        if (!_queue.TryDequeue(out TimedAwaiter awaiter))
                        {
                            break;
                        }
                        queue.Enqueue(awaiter);
                    }

                    while (queue.TryDequeue(out TimedAwaiter awaiter))
                    {
                        awaiter.Run();
                    }
                }
            });
            _thread.Start();
        }

        public TimedAwaiter Delay()
        {
            TimedAwaiter awaiter = new TimedAwaiter(this);
            _queue.Enqueue(awaiter);
            return awaiter;
        }

        public void Dispose()
        {
            _threadRunning = false;
        }
    }
}
